'''
 * CaRComp Execution Environment
 * 
 * Copyright (C) 2011 Taylor L. Riche
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
'''

from carcomp.framework import application, component
from carcomp.framework.net_io import *
from carcomp.framework.quorum import gen_quorum
from carcomp.directory import cameras
from carcomp.apps.ft.messages import client_msgs, agree_msgs

class ServerTrans(component.Implementation):
    
    def __init__(self, name, server):
        component.Implementation.__init__(self, name)
        self.__server__ = server
    
    def trans_msg(self, msg):
        message = agree_msgs.Message(msg)
        if message.cmd == agree_msgs.FTCMD_EXEC:
            self.send_out('out', message.internals)
            print('Checking for cp: ' + str(message.cp))
            if message.cp == 1:
                saved_state = self.__server__.state
                print('Replying with CP: ' + str(saved_state))
                new_message = agree_msgs.CPMsg(message.seq_no, str(saved_state))
                self.send_out('net_out', (cameras.BCAST_AGREES2_NAME, new_message.get_body()))
    
    def out_trans_msg(self, msg):
        self.send_out('net_out', (cameras.QS_NAME, msg[1]))

class EchoServerComp(component.Implementation):
    
    def __init__(self, name):
        component.Implementation.__init__(self, name)
        self.state = 0
    
    def process_msg(self, msg):
        print ('Received from client: ' + str(msg))
        message = client_msgs.Message(msg)
        response = (message._node_name, message.get_body())
        self.state += 1
        self.send_out('out', response)

class EchoServer(application.Application):
    '''
    ACFT Server application
    '''
    
    def __init__(self):
        application.Application.__init__(self)
        
    def setup(self):

        q_comp = gen_quorum.GenericQuorum('q_comp', cameras.local_directory.agree_q_thresh)
        self.create_component(q_comp, [('in', 'process_msg'), ('q_found', '__OUT__')])
        echo_comp = EchoServerComp('echo_comp')
        self.create_component(echo_comp, [('in','process_msg'), ('out','__OUT__')])
        trans_comp = ServerTrans('trans_comp', echo_comp)
        self.create_component(trans_comp, [('in', 'trans_msg'), ('out_trans', 'out_trans_msg'), 
                                           ('out', '__OUT__'), ('net_out', '__OUT__')])
        nr = net_reader.NetReaderComponent(self._node_name, cameras.local_directory)
        nw = net_writer.NetWriterComponent('netout', self._node_name, cameras.local_directory)

        self.add_components([nr, nw])
        
        nr_to_q = self.create_connection((self._node_name, 'out'), ('q_comp', 'in'))
        q_to_trans = self.create_connection(('q_comp', 'q_found'), ('trans_comp', 'in'))
        trans_to_echo = self.create_connection(('trans_comp', 'out'), ('echo_comp', 'in'))
        #echo_to_trans = self.create_connection(('echo_comp', 'out'), ('trans_comp', 'out_trans'))
        #nr_to_echo = self.create_connection((self._node_name, 'out'), ('echo_comp', 'in'))
        #nr_to_trans = self.create_connection((self._node_name, 'out'), ('trans_comp', 'in'))
        echo_to_nw = self.create_connection(('echo_comp', 'out'), ('netout', 'in'))
        #trans_to_nw = self.create_connection(('trans_comp', 'net_out'), ('netout', 'in'))

        nr_thread = application.CompThread(nr, [nr_to_q], True)
        nw_thread = application.CompThread(nw, [echo_to_nw])
        echo_thread = application.CompThread(echo_comp, [trans_to_echo, echo_to_nw])
        trans_thread = application.CompThread(trans_comp, [nr_to_q, q_to_trans, trans_to_echo])
        q_thread = application.CompThread(q_comp, [nr_to_q, q_to_trans])

        self.add_threads([nr_thread, nw_thread, echo_thread, trans_thread, q_thread])
        
if __name__ == '__main__':
    server = EchoServer()
    server.parse_opts()
    server.setup()
    server.execute()
